In angular.dev
official documentation, I found the following statement
Always prefer using the host property over @HostBinding and @HostListener. These decorators exist exclusively for backwards compatibility.
HostBinding
allows us to bind host properties and attributes to properties and methods.
@Component({
/* ... */
})
export class CustomSlider {
@HostBinding('attr.aria-valuenow')
value: number = 0;
@HostBinding('tabIndex')
getTabIndex() {
return this.disabled ? -1 : 0;
}
/* ... */
}
HostListener
allows us to bind host event listeners to methods.
export class CustomSlider {
@HostListener('keydown', ['$event'])
updateValue(event: KeyboardEvent) {
/* ... */
}
}
I suppose host properties are preferred because they can work with signals and signal inputs. On the other hand, HostBinding and HostListener, are designed to work with primitives and methods.
In my demo, I showed how to bind signal inputs to style object, style property, class property, and attributes.
import { Component, signal } from '@angular/core';
import { filter, fromEvent, map } from 'rxjs';
import { toSignal } from '@angular/core/rxjs-interop';
import KeyComponent from './key.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [KeyComponent],
template: `
<h1>Hello from {{ name }}!</h1>
<h2>{{ description }}</h2>
<div class="container">
@for(v of keys(); track v) {
@let isPressed = key() === v;
@let bgColor = isPressed ? ‘yellow’ : ‘white’;
<app-key [value]="v" [isPressed]="isPressed"
[ariaLabel]="v + ' key'" [bgColor]="bgColor"
/>
}
</div>
`,
})
export class App {
name = 'iTHome Ironman 2024 day 14';
description = 'Bind signal inputs to host element';
keys = signal('asdfghjkl');
key = toSignal(fromEvent(document, 'keyup').pipe(
filter((evt) => evt instanceof KeyboardEvent),
map((evt) => evt as KeyboardEvent),
map((evt) => evt.key.toLowerCase()),
filter((key) => 'asdfghjkl'.indexOf(key) >= 0)
), { initialValue: '' });
}
The App
component displays nine squares with letters a, s, d, f, g, h, j, k l respectively. When a user presses any of the above keys, the corresponding square changes the CSS styling. The component listens to a keyup
event and calls toSignal
to create a key signal. The signal value is binded to the KeyComponent
's isPressed
input. When the letter matches the key pressed, the bgColor
also has a different background color. I use Angular 18's let
syntax to evaluate the expressions and assign the results to the local template variables.
<div>
<span>{{ value() }}</span>
</div>
The KeyComponent
displays the letter in the template.
styles: `
:host {
display: flex;
justify-content: center;
align-items: center;
border: 1px solid black;
font-size: 2rem;
width: 100px;
height: 100px;
}
:host-context(.hit) {
border-color: rebeccapurple;
border-width: 0.4rem;
border-radius: 0.5rem;
}`
The host element specifies a flex box to place the letter at the center of the container. It also has a hit CSS class to change the border color, width, and radius.
host: {
'[style.background-color]': 'bgColor()',
'[class.hit]': 'isPressed()',
'[attr.label]': 'ariaLabel()',
'[style]': styleObject()',
},
The host property binds style property, class name, attribute, and style object to signal inputs and a computed signal.
export default class KeyComponent {
value = input.required<string>();
isPressed = input.required<boolean>();
ariaLabel = input.required<string>();
bgColor = input.required<string>();
styleObject = computed(() => {
const fontSize = this.isPressed() ? 2.5 : 2;
const color = this.isPressed() ? 'cyan' : 'black';
return {
fontSize: `${fontSize}rem`,
color,
}
});
}
The KeyComponent component has four signal inputs and a computed signal. The background-color
style property binds to the bgColor
input to have either yellow or white color. The host class, hit
, binds to the isPressed
input to change the border color, width and radius. The label
attribute binds to the ariaLabel
input and displays "{letter} key". When you inspect the host element, the attribute value is seen. The [style]
object binds to the styleObject
computed object to change the text color and increase the font size.
This wraps up day 14 of the ironman challenge.
Host Element Documentation: https://angular.dev/guide/components/host-elements#binding-to-the-host-element
Demo: https://stackblitz.com/edit/stackblitz-starters-reurma?file=src%2Fkey.componen.ts